{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Solving Multiple Tasks in a Sequence of Async Chats\n",
    "\n",
    "This notebook showcases how to use the new chat interface of conversational agents in AutoGen: a_initiate_chats, to conduct a series of tasks. Similar to \"notebook/agentchat_microsoft_fabric.ipynb\", this new interface allows one to pass multiple tasks and their corresponding dedicated agents and execute concurrently. Depending on the prerequisite task(s), the tasks will be solved concurrently, with the summaries from prerequisite task(s) provided to subsequent tasks as context, if the `summary_method` argument is specified.\n",
    "\n",
    "\\:\\:\\:info Requirements\n",
    "\n",
    "Install `ag2`:\n",
    "```bash\n",
    "pip install ag2[openai]\n",
    "```\n",
    "\n",
    "For more information, please refer to the [installation guide](https://docs.ag2.ai/latest/docs/user-guide/basic-concepts/installing-ag2).\n",
    "\n",
    "\\:\\:\\:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import autogen\n",
    "\n",
    "llm_config = autogen.LLMConfig.from_json(path=\"OAI_CONFIG_LIST\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\\:\\:\\:tip\n",
    "\n",
    "Learn more about the various ways to configure LLM endpoints [here](https://docs.ag2.ai/latest/docs/user-guide/basic-concepts/llm-configuration).\n",
    "\n",
    "\\:\\:\\:"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example Tasks\n",
    "Below are four example tasks, with each task being a string of text describing the request. The completion of later tasks requires or benefits from the results of prerequisite tasks.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "financial_tasks = [\n",
    "    \"\"\"What are the current stock prices of NVDA and TESLA, and how is the performance over the past month in terms of percentage change?\"\"\",\n",
    "    \"\"\"Investigate possible reasons of the stock performance.\"\"\",\n",
    "    \"\"\"Plot a graph comparing the stock prices over the past month.\"\"\",\n",
    "]\n",
    "\n",
    "writing_tasks = [\"\"\"Develop an engaging blog post using any information provided.\"\"\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scenario 1: Solve the tasks with a series of chats\n",
    "\n",
    "The `initiate_chats` interface can take a list of dictionaries as inputs. Each dictionary preserves the following fields: \n",
    "- `message`: is a string of text (typically a message containing the task);\n",
    "- `recipient`: a conversable agent dedicated for the task;\n",
    "- `summary_method`: A string specifying the method to get a summary from the chat. Currently supported choices include `last_msg`, which takes the last message from the chat history as the summary, and `reflection_with_llm`, which uses an LLM call to reflect on the chat history and summarize a takeaway;\n",
    "- `summary_prompt`: A string specifying how to instruct an LLM-backed agent (either the recipient or the sender in the chat) to reflect on the chat history and derive a summary. If not otherwise specified, a default prompt will be used when `summary_method` is `reflection_with_llm`.\n",
    "\"Summarize the takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out.\"\n",
    "- `carryover`: A string or a list of string to specify additional context to be used in the chat. With `initiate_chats`, summary from previous chats will be added as carryover. They will be appended after the carryover provided by the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "financial_assistant = autogen.AssistantAgent(\n",
    "    name=\"Financial_assistant\",\n",
    "    llm_config=llm_config,\n",
    ")\n",
    "research_assistant = autogen.AssistantAgent(\n",
    "    name=\"Researcher\",\n",
    "    llm_config=llm_config,\n",
    ")\n",
    "writer = autogen.AssistantAgent(\n",
    "    name=\"writer\",\n",
    "    llm_config=llm_config,\n",
    "    system_message=\"\"\"\n",
    "        You are a professional writer, known for\n",
    "        your insightful and engaging articles.\n",
    "        You transform complex concepts into compelling narratives.\n",
    "        Reply \"TERMINATE\" in the end when everything is done.\n",
    "        \"\"\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user = autogen.UserProxyAgent(\n",
    "    name=\"User\",\n",
    "    human_input_mode=\"NEVER\",\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\") and x.get(\"content\", \"\").rstrip().endswith(\"TERMINATE\"),\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"tasks\",\n",
    "        \"use_docker\": False,\n",
    "    },  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n",
    ")\n",
    "\n",
    "chat_results = await user.a_initiate_chats([\n",
    "    {\n",
    "        \"chat_id\": 1,\n",
    "        \"recipient\": financial_assistant,\n",
    "        \"message\": financial_tasks[0],\n",
    "        \"silent\": False,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\n",
    "        \"chat_id\": 2,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": research_assistant,\n",
    "        \"message\": financial_tasks[1],\n",
    "        \"silent\": False,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\n",
    "        \"chat_id\": 3,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": financial_assistant,\n",
    "        \"message\": financial_tasks[2],\n",
    "        \"silent\": False,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\"chat_id\": 4, \"prerequisites\": [1, 2, 3], \"recipient\": writer, \"silent\": False, \"message\": writing_tasks[0]},\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Check chat results\n",
    "The `initiate_chat` method returns a `ChatResult` object, which is a dataclass object storing information about the chat. Currently, it includes the following attributes:\n",
    "\n",
    "- `chat_history`: a list of chat history.\n",
    "- `summary`: a string of chat summary. A summary is only available if a summary_method is provided when initiating the chat.\n",
    "- `cost`: a tuple of (total_cost, total_actual_cost), where total_cost is a dictionary of cost information, and total_actual_cost is a dictionary of information on the actual incurred cost with cache.\n",
    "- `human_input`: a list of strings of human inputs solicited during the chat. (Note that since we are setting `human_input_mode` to `NEVER` in this notebook, this list is always empty.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i, chat_res in chat_results.items():\n",
    "    print(f\"*****{i}th chat*******:\")\n",
    "    print(chat_res.summary)\n",
    "    print(\"Human input in the middle:\", chat_res.human_input)\n",
    "    print(\"Conversation cost: \", chat_res.cost)\n",
    "    print(\"\\n\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scenario 2: With human inputs revising tasks in the middle\n",
    "\n",
    "Since AutoGen agents support soliciting human inputs during a chat if `human_input_mode` is specified properly, the actual task might be revised in the middle of a chat. \n",
    "\n",
    "The example below showcases that even if a task is revised in the middle (for the first task, the human user requests to get Microsoft's stock price information as well, in addition to NVDA and TSLA), the `reflection_with_llm`` summary method can still capture it, as it reflects on the whole conversation instead of just the original request."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user = autogen.UserProxyAgent(\n",
    "    name=\"User\",\n",
    "    human_input_mode=\"ALWAYS\",  # ask human for input at each step\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\") and x.get(\"content\", \"\").rstrip().endswith(\"TERMINATE\"),\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"tasks\",\n",
    "        \"use_docker\": False,\n",
    "    },  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n",
    ")\n",
    "\n",
    "chat_results = await user.a_initiate_chats([\n",
    "    {\n",
    "        \"chat_id\": 1,\n",
    "        \"recipient\": financial_assistant,\n",
    "        \"message\": financial_tasks[0],\n",
    "        \"silent\": False,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\n",
    "        \"chat_id\": 2,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": research_assistant,\n",
    "        \"message\": financial_tasks[1],\n",
    "        \"silent\": True,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\n",
    "        \"chat_id\": 3,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": financial_assistant,\n",
    "        \"message\": financial_tasks[2],\n",
    "        \"silent\": False,\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\"chat_id\": 4, \"prerequisites\": [1, 2, 3], \"recipient\": writer, \"message\": writing_tasks[0]},\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Check chat results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i, chat_res in chat_results.items():\n",
    "    print(f\"*****{i}th chat*******:\")\n",
    "    print(chat_res.summary)\n",
    "    print(\"Human input in the middle:\", chat_res.human_input)\n",
    "    print(\"Conversation cost: \", chat_res.cost)\n",
    "    print(\"\\n\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scenario 3: Solve the tasks with a series of chats involving group chat"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_proxy = autogen.UserProxyAgent(\n",
    "    name=\"User_proxy\",\n",
    "    system_message=\"A human admin.\",\n",
    "    human_input_mode=\"NEVER\",\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\") and x.get(\"content\", \"\").rstrip().endswith(\"TERMINATE\"),\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"groupchat\",\n",
    "        \"use_docker\": False,\n",
    "    },\n",
    ")\n",
    "\n",
    "research_assistant = autogen.AssistantAgent(\n",
    "    name=\"Researcher\",\n",
    "    llm_config=llm_config,\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\") and x.get(\"content\", \"\").rstrip().endswith(\"TERMINATE\"),\n",
    ")\n",
    "\n",
    "writer = autogen.AssistantAgent(\n",
    "    name=\"Writer\",\n",
    "    llm_config=llm_config,\n",
    "    system_message=\"\"\"\n",
    "    You are a professional writer, known for\n",
    "    your insightful and engaging articles.\n",
    "    You transform complex concepts into compelling narratives.\n",
    "    Reply \"TERMINATE\" in the end when everything is done.\n",
    "    \"\"\",\n",
    ")\n",
    "\n",
    "critic = autogen.AssistantAgent(\n",
    "    name=\"Critic\",\n",
    "    system_message=\"\"\"Critic. Double check plan, claims, code from other agents and provide feedback. Check whether the plan includes adding verifiable info such as source URL.\n",
    "    Reply \"TERMINATE\" in the end when everything is done.\n",
    "    \"\"\",\n",
    "    llm_config=llm_config,\n",
    ")\n",
    "\n",
    "groupchat_1 = autogen.GroupChat(agents=[user_proxy, research_assistant, critic], messages=[], max_round=50)\n",
    "\n",
    "groupchat_2 = autogen.GroupChat(agents=[user_proxy, writer, critic], messages=[], max_round=50)\n",
    "\n",
    "manager_1 = autogen.GroupChatManager(\n",
    "    groupchat=groupchat_1,\n",
    "    name=\"Research_manager\",\n",
    "    llm_config=llm_config,\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"groupchat\",\n",
    "        \"use_docker\": False,\n",
    "    },\n",
    ")\n",
    "manager_2 = autogen.GroupChatManager(\n",
    "    groupchat=groupchat_2,\n",
    "    name=\"Writing_manager\",\n",
    "    llm_config=llm_config,\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"groupchat\",\n",
    "        \"use_docker\": False,\n",
    "    },\n",
    ")\n",
    "\n",
    "user = autogen.UserProxyAgent(\n",
    "    name=\"User\",\n",
    "    human_input_mode=\"NEVER\",\n",
    "    is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n",
    "    code_execution_config={\n",
    "        \"last_n_messages\": 1,\n",
    "        \"work_dir\": \"tasks\",\n",
    "        \"use_docker\": False,\n",
    "    },  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n",
    ")\n",
    "await user.a_initiate_chats([\n",
    "    {\"chat_id\": 1, \"recipient\": research_assistant, \"message\": financial_tasks[0], \"summary_method\": \"last_msg\"},\n",
    "    {\n",
    "        \"chat_id\": 2,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": manager_1,\n",
    "        \"message\": financial_tasks[1],\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\n",
    "        \"chat_id\": 3,\n",
    "        \"prerequisites\": [1],\n",
    "        \"recipient\": manager_1,\n",
    "        \"message\": financial_tasks[2],\n",
    "        \"summary_method\": \"reflection_with_llm\",\n",
    "    },\n",
    "    {\"chat_id\": 4, \"prerequisites\": [1, 2, 3], \"recipient\": manager_2, \"message\": writing_tasks[0]},\n",
    "])"
   ]
  }
 ],
 "metadata": {
  "front_matter": {
   "description": "Use conversational agents to solve a set of tasks with a sequence of async chats.",
   "tags": [
    "orchestration",
    "async",
    "sequential chats"
   ]
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  },
  "vscode": {
   "interpreter": {
    "hash": "949777d72b0d2535278d3dc13498b2535136f6dfe0678499012e853ee9abcab1"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
